import { Config } from './config'
import { Board } from './board'

export class Boards {
  /**
   * 创建一个看板
   * @param {JQuery} $el 看板容器元素
   * @param {BoardsSettings} options 配置
   */
  constructor($el, options) {
    const config = new Config(options)

    this.$el = $el
    this.config = config

    this.boards = {}
    this.$hoverIssue = null
    this.$selectedIssue = null
    this.timerForScrollLoad = null

    this.bindDrag()
    this.bindScroll()
    this.initPlugins()
    this.setData(config.data)
  }

  initPlugins() {
    this.initSortable()
  }

  // 初始化拖拽排序功能
  initSortable() {
    const that = this
    const Sortable = that.config.plugins.Sortable

    // 如果没有注入 SortableJS 依赖，则不启用这个功能
    if (!Sortable) {
      return false
    }
    that.$el.addClass('boards-sortable')
    that.sortable = Sortable.create(that.$el[0], {
      handle: '.board-title',
      dataIdAttr: 'data-state',
      onUpdate() {
        that.config.onSorted(that.sortable.toArray())
      }
    })
    that.$el.on('click', '.board-action', function (e) {
      const $target = $(this)
      const id = $target.data('id')
      const state = $target.parents('.board').data('state')
      let actions = that.config.actions

      if (typeof actions === 'function') {
        actions = actions(that.config)
      }
      actions.some((action) => {
        if (action.id === id && action.callback) {
          action.callback(that, that.boards[state], e)
          return true
        }
        return false
      })
    })
    return true
  }

  bindScroll() {
    const that = this
    let timerForScrollLoad = null

    function onScrollLoad() {
      if (timerForScrollLoad) {
        clearTimeout(timerForScrollLoad)
      }
      timerForScrollLoad = setTimeout(function () {
        timerForScrollLoad = null
        that.load()
      }, 200)
    }

    $(window).on('resize', onScrollLoad)
    this.$el.on('scroll', onScrollLoad)
  }

  bindDrag() {
    const that = this

    if (that.config.readonly) {
      return
    }
    that.$el.on('dragstart', '.card', function (e) {
      const issueId = $(this).data('id')
      const issue = that.getIssue(issueId)

      if (issue) {
        that.setFocus(issue.type, issue.state)
      }
      that.$selectedIssue = $(this)
      e.originalEvent.dataTransfer.setData('text/plain', issueId)
      e.stopPropagation()
    })
    that.$el.on('dragend', '.card', function () {
      that.clearFocus()
    })
    that.$el.on('drop', '.board-list', function (e) {
      e.preventDefault()
      e.stopPropagation()

      if (that.$hoverIssue) {
        that.$hoverIssue.removeClass('card-dragover')
      }
      if (!that.$selectedIssue) {
        return false
      }
      if (that.config.readonly) {
        return false
      }

      const $issue = that.$selectedIssue
      const issueId = $issue.data('id')
      const $board = $(this).parents('.board')
      const state = $board.data('state')
      const oldState = $issue.parents('.board').data('state')
      const nextIssueId = that.$hoverIssue ? that.$hoverIssue.data('id') : null

      that.setIssueState(issueId, state, nextIssueId, async function (issue) {
        return that.config.onUpdate(issue, oldState, nextIssueId)
      })
      return true
    })
    that.$el.on('dragover', '.board-list', function (e) {
      const key = that.config.key
      let $target = $(e.target)

      if (!that.$selectedIssue) {
        return
      }

      e.preventDefault()
      while ($target.length > 0 && !$target.hasClass('card')) {
        $target = $target.parent()
      }
      if ($target.length < 1) {
        if (that.$hoverIssue) {
          that.$hoverIssue.removeClass('card-dragover')
        }
        that.$hoverIssue = null
        return
      }
      if (that.$hoverIssue) {
        that.$hoverIssue.removeClass('card-dragover')
      }
      that.$hoverIssue = $target
      const hoverIssue = that.getIssue($target.data('id'))
      const selectedIssue = that.getIssue(that.$selectedIssue.data('id'))
      if (
        hoverIssue && selectedIssue
        && hoverIssue[key] !== selectedIssue[key]
      ) {
        that.$hoverIssue.addClass('card-dragover')
      }
    })
  }

  /**
   * 设置焦点板块
   * 根据指定的类型和状态，排除无关状态的板块
   * @param {String, Number} typeId 任务类型 id
   * @param {String, Number} stateId 任务状态 id
   */
  setFocus(typeId, stateId) {
    let stateIds = []
    let issueType = null

    if (this.config.key !== 'state') {
      return
    }
    this.config.types.some(function (t) {
      if (t.id === typeId) {
        issueType = t
        return true
      }
      return false
    })
    if (issueType) {
      stateIds = issueType.states.map(state => state.id.toString())
    }
    Object.keys(this.boards).forEach((id) => {
      if (id !== stateId.toString() && stateIds.indexOf(id) === -1) {
        this.boards[id].$el.addClass('board-blur')
      }
    })
  }

  /* 清除板块的焦点效果 */
  clearFocus() {
    Object.keys(this.boards).forEach((key) => {
      this.boards[key].$el.removeClass('board-blur')
    })
  }

  getIssueCard(id) {
    return $(`#${this.config.name}-issue-${id}`)
  }

  getIssue(id) {
    const $issue = this.getIssueCard(id)
    const $board = $issue.parents('.board')
    const state = $board.data('state')
    const board = this.boards[state]
    if (board) {
      return board.get(id)
    }
    return null
  }

  removeIssue(id) {
    let issue = null
    const $issue = this.getIssueCard(id)

    if ($issue.length < 1) {
      return issue
    }

    const state = $issue.parents('.board').data('state')
    if (state) {
      const board = this.boards[state]
      if (board) {
        issue = board.remove(id)
      }
    }

    $issue.remove()
    return issue
  }

  updateIssue(issue) {
    const board = this.boards[issue[this.config.key]]

    if (board) {
      board.updateIssue(issue)
      return true
    }
    return false
  }

  prependIssue(issue) {
    const board = this.boards[issue[this.config.key]]

    if (board) {
      board.prependIssue(issue)
      return true
    }
    return false
  }

  setIssueState(issueId, state, nextIssueId, callback) {
    const $issue = this.getIssueCard(issueId)
    const $nextIssue = nextIssueId ? this.getIssueCard(nextIssueId) : null

    if ($issue.length < 1) {
      return null
    }

    const user = this.config.user
    const $oldBoard = $issue.parents('.board')
    const oldState = $oldBoard.data('state')
    const oldBoard = this.boards[oldState]
    const newBoard = this.boards[state]

    if (oldBoard.state === state) {
      return null
    }

    // 如果新的板块不接受该状态的 issue
    if (newBoard.exclude && newBoard.exclude.indexOf(oldState) >= 0) {
      return null
    }

    const issue = oldBoard.get(issueId)
    // 如果当前用户既不具备管理权限，也不是 issue 作者，则禁止操作
    if (!user.admin && issue.author.id !== user.id) {
      return null
    }

    issue[this.config.key] = state
    newBoard.add(issue)
    oldBoard.remove(issue.id)
    $issue.hide(256, function () {
      // 如果有指定下一个 issue，则将当前 issue 插入到它前面
      if ($nextIssue) {
        $nextIssue.before($issue)
      } else {
        newBoard.$issues.prepend($issue)
      }
      $issue.addClass('card-loading')
      $issue.show(256, async function () {
        if (callback) {
          await callback(issue)
        }
        $issue.removeClass('card-loading')
      })
    })
    newBoard.updateTip()
    oldBoard.updateTip()
    oldBoard.autoload()

    return issue
  }

  load() {
    let count = 0
    const bound = this.$el.offset()

    // 设置边界框（可见区域）的尺寸
    bound.width = this.$el.width()
    bound.height = this.$el.height()

    Object.keys(this.boards).forEach((state) => {
      const board = this.boards[state]
      const offset = board.$el.offset()
      // 如果当前板块在可见区域内
      if (
        offset.top + board.$el.height() > bound.top
        && offset.left + board.$el.width() > bound.left
        && offset.top < bound.top + bound.height
        && offset.left < bound.left + bound.width
      ) {
        if (board.firstload()) {
          count += 1
        }
      }
    })
    return count
  }

  setData(data) {
    this.boards = {}
    this.$el.addClass('boards-list').empty()

    data.forEach((boardData) => {
      const board = new Board(boardData, this.config)
      this.boards[boardData.state] = board
      this.$el.append(board.$el)
    })
  }

  clearData() {
    Object.keys(this.boards).forEach((state) => {
      this.boards[state].clear()
    })
  }
}

export default null
